package com.googlecode.esblabs.util.mule.transformer.jaxb;
import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI;

import java.io.InputStream;
import java.io.OutputStream;
import java.io.Writer;
import java.net.URL;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.PropertyException;
import javax.xml.bind.Unmarshaller;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;

import org.xml.sax.SAXException;


public class XmlBinding {

	static public void marshal(Object o, Writer w) {
        try {
			JAXBContext context = JAXBContext.newInstance(o.getClass());
			Marshaller m = context.createMarshaller();
			m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
			m.marshal(o, w);
			
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	static public void marshal(Object o, OutputStream os) {
        try {
			// Illustrate two methods to create JAXBContext for j2s binding.
			// (1) by root classes newInstance(Class ...)
			JAXBContext context = JAXBContext.newInstance(o.getClass());

//          // (2) by package, requires jaxb.index file in package cardfile.
//          //     newInstance(String packageNames)
//          JAXBContext context2 = JAXBContext.newInstance("cardfile");

			Marshaller m = context.createMarshaller();
			m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
			m.marshal(o, os);
			
		} catch (PropertyException e) {
			throw new RuntimeException(e);
		} catch (JAXBException e) {
			throw new RuntimeException(e);
		}
	}

    @SuppressWarnings("unchecked")
	static public Object unmarshal(Class c, InputStream is) {
    	return unmarshal(c, is, null);
	}

    @SuppressWarnings("unchecked")
	static public Object unmarshal(Class c, InputStream is, String xsdResource) {
		try {
			JAXBContext context = JAXBContext.newInstance(c);

			Unmarshaller um = context.createUnmarshaller();
			
			if (xsdResource != null) um.setSchema(getSchema(xsdResource));

			return um.unmarshal(is);
			
		} catch (JAXBException e) {
			throw new RuntimeException(e);
		}
	}

	/** returns a JAXP 1.3 schema by parsing the specified resource. */
    static private Schema getSchema(String schemaResourceName) {
        SchemaFactory sf = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI);

        try {
            URL schemaURL = XmlBinding.class.getResource(schemaResourceName);

            // Simply skip out if the schema is missing...
            if (schemaURL == null) {
                System.err.println("SCHEMA NOT FOUND, RETURN NULL");
            	return null;
            }
            
            return sf.newSchema(schemaURL);
        } catch (SAXException se) {
            // this can only happen if there's a deployment error and the resource is missing.
            throw new RuntimeException(se);
        }
    }
}
